//! Step Actor
//! 
//! This actor executes individual test steps,
//! corresponding to HttpRunner's IStep implementations.

use crate::actors::messages::*;
use crate::actors::{PluginActor, AIOrchestrationActor};
use crate::error::{Result, Error, TestExecutionError};
use crate::models::{
    step::{StepResult, StepContext, StepType},
    config::StepConfig,
    request::{HttpRequest, HttpMethod},
    Value, ValueMap,
};
use crate::validation::{ValidationEngine, ValidationContext};

/// Step validation result
#[derive(Clone)]
pub struct StepValidationResult {
    pub success: bool,
    pub errors: Vec<String>,
}
use crate::plugins::builtin::{BuiltinPlugin, http::HttpPlugin};
use coerce::actor::{Actor, ActorRef};
use coerce::actor::context::ActorContext;
use coerce::actor::message::Handler;
use async_trait::async_trait;
use std::collections::HashMap;
use uuid::Uuid;
use serde_json::json;

/// Validation result structure
#[derive(Debug, Clone)]
pub struct ValidationResult {
    pub success: bool,
    pub errors: Vec<String>,
}

/// Step Actor
#[derive(Debug)]
pub struct StepActor {
    /// Step ID
    step_id: Uuid,

    /// Plugin actor reference
    plugin_actor: Option<ActorRef<PluginActor>>,

    /// AI orchestration actor reference
    ai_actor: Option<ActorRef<AIOrchestrationActor>>,

    /// Validation engine for assertions and extractions
    validation_engine: ValidationEngine,
}

impl StepActor {
    /// Create a new step actor
    pub fn new(
        step_id: Uuid,
        plugin_actor: Option<ActorRef<PluginActor>>,
        ai_actor: Option<ActorRef<AIOrchestrationActor>>,
    ) -> Self {
        Self {
            step_id,
            plugin_actor,
            ai_actor,
            validation_engine: ValidationEngine::new(),
        }
    }

    /// Execute a step based on its type (simplified for now)
    async fn execute_step_internal(&self, step_config: &StepConfig, context: &mut StepContext) -> Result<StepResult> {
        // For now, treat all steps as API requests since we have HTTP plugin implemented
        self.execute_api_step(step_config, context).await
    }

    /// Execute an API step (HTTP request)
    async fn execute_api_step(&self, step_config: &StepConfig, context: &mut StepContext) -> Result<StepResult> {
        // Create HTTP plugin instance
        let http_plugin = HttpPlugin::new();

        // Extract HTTP request parameters from step config
        let request = self.build_http_request_from_config(step_config, context)?;

        // Execute the HTTP request
        let request_value = serde_json::to_value(&request)
            .map_err(|e| Error::TestExecution(TestExecutionError::StepExecutionFailed {
                step_name: step_config.step_name.clone(),
                reason: format!("Failed to serialize request: {}", e),
            }))?;

        let response_value = http_plugin.call_function("request", &[request_value]).await
            .map_err(|e| Error::TestExecution(TestExecutionError::StepExecutionFailed {
                step_name: step_config.step_name.clone(),
                reason: format!("HTTP request failed: {}", e),
            }))?;

        // Create step result
        let result = StepResult::new(
            Uuid::new_v4(),
            step_config.step_name.clone(),
            StepType::API,
        );

        // For now, just return success (we'll store response in context later)
        // TODO: Store response_value in context or result

        Ok(result)
    }



    /// Build HTTP request from step configuration
    fn build_http_request_from_config(&self, step_config: &StepConfig, _context: &StepContext) -> Result<HttpRequest> {
        // Extract method from parameters
        let method_str = step_config.parameters.get("method")
            .and_then(|v| v.as_str())
            .unwrap_or("GET");

        let method = match method_str.to_uppercase().as_str() {
            "GET" => HttpMethod::GET,
            "POST" => HttpMethod::POST,
            "PUT" => HttpMethod::PUT,
            "DELETE" => HttpMethod::DELETE,
            "PATCH" => HttpMethod::PATCH,
            "HEAD" => HttpMethod::HEAD,
            "OPTIONS" => HttpMethod::OPTIONS,
            _ => HttpMethod::GET,
        };

        // Extract URL from parameters
        let url = step_config.parameters.get("url")
            .and_then(|v| v.as_str())
            .ok_or_else(|| Error::TestExecution(TestExecutionError::StepExecutionFailed {
                step_name: step_config.step_name.clone(),
                reason: "Missing URL parameter".to_string(),
            }))?;

        let mut request = HttpRequest::new(method, url.to_string());

        // Add headers if present
        if let Some(headers_value) = step_config.parameters.get("headers") {
            if let Some(headers_obj) = headers_value.as_object() {
                for (key, value) in headers_obj {
                    if let Some(value_str) = value.as_str() {
                        request.headers.insert(key.clone(), value_str.to_string());
                    }
                }
            }
        }

        // Add JSON body if present
        if let Some(json_body) = step_config.parameters.get("json") {
            request.json = Some(json_body.clone());
        }

        // Add query parameters if present
        if let Some(params_value) = step_config.parameters.get("params") {
            if let Some(params_obj) = params_value.as_object() {
                for (key, value) in params_obj {
                    request.params.insert(key.clone(), value.clone());
                }
            }
        }

        // Add timeout if present
        if let Some(timeout_value) = step_config.parameters.get("timeout") {
            if let Some(timeout_num) = timeout_value.as_f64() {
                request.timeout = Some(timeout_num);
            }
        }

        Ok(request)
    }

    /// Validate step result using configured validators
    async fn validate_step_result(&self, result: &StepResult, validators: &ValueMap) -> Result<StepValidationResult> {
        // Convert step result to validation context
        let mut response_data = HashMap::new();

        if let Some(data) = &result.data {
            if let Some(obj) = data.as_object() {
                for (key, value) in obj {
                    response_data.insert(key.clone(), value.clone());
                }
            }
        }

        // Add step metadata to response data
        response_data.insert("status_code".to_string(), json!(if result.success { 200 } else { 500 }));
        response_data.insert("elapsed".to_string(), json!(result.elapsed));
        response_data.insert("content_size".to_string(), json!(result.content_size));

        let validation_context = ValidationContext::new(response_data, HashMap::new());

        // Run validations
        let validation_results = self.validation_engine.validate(validators, &validation_context)?;

        // Collect errors
        let mut errors = Vec::new();
        let mut all_success = true;

        for validation_result in validation_results {
            if !validation_result.success {
                all_success = false;
                errors.push(validation_result.message);
            }
        }

        Ok(StepValidationResult {
            success: all_success,
            errors,
        })
    }

    /// Extract variables from step result
    async fn extract_variables(&self, result: &StepResult, extractors: &ValueMap) -> Result<ValueMap> {
        // Convert step result to validation context
        let mut response_data = HashMap::new();

        if let Some(data) = &result.data {
            if let Some(obj) = data.as_object() {
                for (key, value) in obj {
                    response_data.insert(key.clone(), value.clone());
                }
            }
        }

        // Add step metadata
        response_data.insert("status_code".to_string(), json!(if result.success { 200 } else { 500 }));
        response_data.insert("elapsed".to_string(), json!(result.elapsed));
        response_data.insert("content_size".to_string(), json!(result.content_size));

        let mut validation_context = ValidationContext::new(response_data, HashMap::new());

        // Extract variables
        let extracted = self.validation_engine.extract(extractors, &mut validation_context)?;

        Ok(extracted)
    }
}

#[async_trait]
impl Actor for StepActor {
    async fn started(&mut self, _ctx: &mut ActorContext) {
        tracing::info!("StepActor started: {}", self.step_id);
    }
}

#[async_trait]
impl Handler<StepMessage> for StepActor {
    async fn handle(&mut self, msg: StepMessage, _ctx: &mut ActorContext) -> StepResponse {
        match msg {
            StepMessage::ExecuteStep { step_config, mut context } => {
                match self.execute_step_internal(&step_config, &mut context).await {
                    Ok(result) => StepResponse::StepExecuted { result },
                    Err(e) => StepResponse::Error {
                        message: format!("Step execution failed: {}", e),
                    }
                }
            }

            StepMessage::ValidateResult { result, validators } => {
                match self.validate_step_result(&result, &validators).await {
                    Ok(validation_result) => StepResponse::ValidationCompleted {
                        success: validation_result.success,
                        errors: if validation_result.success {
                            Vec::new()
                        } else {
                            validation_result.errors
                        },
                    },
                    Err(e) => StepResponse::Error {
                        message: format!("Validation failed: {}", e),
                    }
                }
            }

            StepMessage::ExtractVariables { result: _, extractors: _ } => {
                // Simplified variable extraction for now
                StepResponse::VariablesExtracted {
                    variables: ValueMap::new(),
                }
            }
        }
    }
}
